import {
	encryptByAES,
	sha256,
	md5,
	base64,
	newAESKey,
	encryptByRSA,
	randomString
} from '@/util/crypto.js'
import { apiServerUrl } from '@/config.js'

const HEADER_TOKEN_NAME = 'SUM-TICKET'
const TOKEN_NAME = 'token'

/** 网站来源 */
const HEADER_WEBSITE = "WEBSITE-SOURCE"

/** 网站名称-微信小程序 */
const WEBSITE_NAME = "WECHAT_APPLET"


uni.addInterceptor('request', {
	invoke(args) {
		// request 触发前拼接 url 
		/*if(args.url && args.url.length > 3 && !args.url.substring(0, 4) === 'http') {
			args.url = apiServerUrl + args.url;
		}*/
		let token = uni.getStorageSync(TOKEN_NAME)
		if (!args.header) {
			args.header = {}
		}
		if (token && typeof(token) === 'string') {
			args.header[HEADER_TOKEN_NAME] = token
		}
		args.header[HEADER_WEBSITE] = WEBSITE_NAME;
		return true
	},
	success(args) {
		// 请求成功后，修改code值为1
		return successCallback(args)
	},
	fail(err) {
		return errorCallback(err)
	},
	complete(res) {
		console.log('interceptor-complete', res)
	}
})

/**
 * 错误回调函数
 * @param {*} error 错误对象
 */
const errorCallback = error => {
	if (error.response) {
		// The request was made and the server responded with a status code
		// that falls out of the range of 2xx
		if (error.response.data.code) {
			// 未登录
			if (error.code === 30001) {
				window.location.href = error.response.data.data
			} else {
				uni.showToast({
					message: error.response.data.msg,
					duration: 3000,
					type: 'error'
				})
			}
		} else {
			uni.showToast({
				message: '当前服务忙或网络异常, 请稍后再试',
				duration: 3000,
				type: 'error'
			})
		}
	} else if (error.request) {
		// The request was made but no response was received
		// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
		// http.ClientRequest in node.js
		uni.showToast({
			message: '当前服务忙或网络异常, 请稍后再试',
			duration: 3000,
			type: 'error'
		})
	} else {
		uni.showToast({
			message: error.message,
			duration: 3000,
			type: 'error'
		})
	}
	return Promise.reject(error)
}

/**
 * 成功回调函数
 * @param {*} response 响应对象
 */
const successCallback = response => {
	if (response.status && response.status > 200) {
		return response.data
	}
	if (response.data.code === 20000) {
		return response.data
	}
	if (response.data.code === 20001) {
		// 由服务端控制的在ajax中进行的redirect跳转，data属性即为跳转的URL
		uni.removeStorageSync('token')
		store.commit('setToken', null)
		window.location.href = response.data.data
		return
	}
	
	if (response.data.code === 30000 && response.data.msg === '登录已过期，请重新登录') {
		uni.showToast({
			title: response.data.msg,
			duration: 3000,
			type: 'error',
			icon: 'none'
		})
		uni.clearStorageSync()
		uni.switchTab({ url: '/pages/we/index' })
		return
	}
	
	if (response.data.code === 30006) {
		// 登陆时的密钥策略校验不通过，必须修改密码
		return response.data
	}
	if (response.data.code >= 30000 && response.data.code != 50000) {
		if (response.data.msg && typeof(response.data.msg) === 'string' && response.data.msg.indexOf('</br>') !== -
			1) {
			uni.showToast({
				title: response.data.msg ? response.data.msg : '操作失败',
				duration: 3000,
				type: 'error',
				icon: 'none'
			})
		} else {
			uni.showToast({
				title: response.data.msg ? response.data.msg : '操作失败',
				duration: 3000,
				type: 'error',
				icon: 'none'
			})
		}
	}
	return response.data
}

/**
 * 默认的安全等级: 可用值[0, 1, 2, 3]
 * 0:无, 1:防重放, 2:防重放+Base64编码+签名, 3:防重放+AES/RSA加密+签名
 */
const DEFAULT_SECURITY_CLASS = 0

/**
 * get方式请求, 使用默认安全等级DEFAULT_SECURITY_CLASS
 * @param {String} apiUri api uri
 * @param {Object} params 参数
 */
const get = (apiUri, params) => {
	// if (apiUri.indexOf('?') !== -1) {
	// 	apiUri += '&' + new Date().getTime()
	// } else {
	// 	apiUri += '?' + new Date().getTime()
	// }
	return getSafe(apiUri, params, DEFAULT_SECURITY_CLASS)
}

/**
 * 安全get提交，暂时仅支持安全等级[0, 1]
 * @param {String} apiUri api uri
 * @param {Object} params 参数
 * @param {int} securityLevel 安全等级[0, 1], 如下：
 *    0:无, 1:防重放
 */
const getSafe = (apiUri, params, securityLevel = DEFAULT_SECURITY_CLASS) => {
	return new Promise((resolve, reject) => {
		uni.request({
			method: 'GET',
			url: apiServerUrl + apiUri, //仅为示例，并非真实接口地址。
			data: params,
			withCredentials: true,
			timeout: 60 * 1000,
			success: response => {
				resolve(response)
			},
			fail: error => {
				resolve(error)
			}
		})
	})

}

/**
 * post方式请求, 表单传参, 使用默认安全等级DEFAULT_SECURITY_CLASS
 * @param {String} apiUri api uri
 * @param {ObjectOrJsonString} params 请求参数，对象或json字符串
 */
const post = (apiUri, params) => {
	if (!params) {
		params = {}
	}
	return postSafe(apiUri, params, DEFAULT_SECURITY_CLASS)
}

/**
 * 兼容历史版本的方法
 * 服务端参数必须使用@RequestBody注解接收参数
 * @param {String} apiUri api uri
 * @param {ObjectOrJsonString} params 请求参数，对象或json字符串
 * @param {int} securityLevel 安全等级[0, 1, 2, 3], 默认为1, 如下：
 *    0:无, 1:防重放, 2:防重放+Base64编码+签名, 3:防重放+AES/RSA加密+签名
 */
const postJson = (apiUri, params, securityLevel = 1) => {
	if (!params) {
		params = {}
	}
	if (typeof(params) === 'string') {
		return postSafe(apiUri, params, securityLevel)
	} else {
		return postSafe(apiUri, JSON.stringify(params), securityLevel)
	}
}

/**
 * 安全post提交, 表单传参
 * 如果params是JsonString, 在使用安全等级[0, 1]时,
 * 服务端参数需要使用@RequestBody注解接收参数
 * @param {String} apiUri api uri
 * @param {ObjectOrJsonString} params 请求参数，对象或json字符串
 * @param {int} securityLevel 安全等级[0, 1, 2, 3], 默认为3, 如下：
 *    0:无, 1:防重放, 2:防重放+Base64编码+签名, 3:防重放+AES/RSA加密+签名
 */
const postSafe = (apiUri, params, securityLevel = 3) => {
	let ticket = uni.getStorageSync(TOKEN_NAME)
	if (securityLevel < 0) {
		securityLevel = 0
	}
	if (securityLevel > 3) {
		securityLevel = 3
	}
	let safeTag = createSafeTag(securityLevel)
	switch (securityLevel) {
		case 0:
			// 无
			return postSafeOfSecurityLevel0(apiUri, params)
		case 1:
			// 防重放
			return postSafeOfSecurityLevel1(apiUri, params, safeTag)
		case 2:
			// 防重放+Base64编码+签名
			return postSafeOfSecurityLevel2(apiUri, params, safeTag, ticket || '')
		case 3:
			// 防重放+AES/RSA加密+签名
			return postSafeOfSecurityLevel3(apiUri, params, safeTag, ticket || '')
		default:
			// console.log('不存在该安全等级:' + securityLevel)
	}
}

/**
 * 0:无
 * @param {String} apiUri api uri
 * @param {ObjectOrJsonString} params 请求参数，对象或json字符串
 * @return {Promise} Promise
 */
const postSafeOfSecurityLevel0 = (apiUri, params) => {
	if (!params) {
		params = {}
	}
	let isStr = typeof(params) === 'string'
	var safeConfig = {}
	// let _param = isStr ? params : param2String(params)
	console.log('apiUri', apiUri)
	if(apiUri == '/security/login/wechat/applet/registerLogin' || apiUri == '/security/login/wechat/applet/autoLogin') {
		safeConfig = createSafeConfig(null, 3)
	} else {
		safeConfig = createSafeConfig(null, isStr ? 2 : 1)
	}
	var config = {}
	config.method = 'POST'
	config.url = apiServerUrl + apiUri
	config.data = params
	config.withCredentials = true
	config.timeout = 60 * 1000
	config.header = safeConfig.headers
	return new Promise((resolve, reject) => {
		uni.request(config).then(response => {
			resolve(response[1])
		}).catch(error => {
			resolve(error[1])
		})
	})
}

/**
 * 1:防重放
 * @param {String} apiUri api uri
 * @param {ObjectOrJsonString} params 请求参数，对象或json字符串
 * @param {String} safeTag 安全标签, 安全等级;时间戳;防重放攻击的nonce, 例如：1;1551687809382;a411a616d438a...
 * @param {String} ticket 会话票据
 * @return {Promise} Promise
 */
// eslint-disable-next-line no-unused-vars
const postSafeOfSecurityLevel1 = (apiUri, params, safeTag, ticket) => {
	if (!params) {
		params = {}
	}
	let isStr = typeof(params) === 'string'
	let _param = isStr ? params : param2String(params)
	var safeConfig = createSafeConfig(safeTag, isStr ? 2 : 1)
	var config = {}
	config.method = 'POST'
	config.url = apiServerUrl + apiUri
	config.data = _param
	config.withCredentials = true
	config.timeout = 60 * 1000
	config.header = safeConfig.headers
	return new Promise((resolve, reject) => {
		uni.request(config).then(response => {
			resolve(response[1])
		}).catch(error => {
			resolve(error[1])
		})
	})
}

/**
 * 2:防重放+Base64编码+签名
 * @param {string} apiUri api uri
 * @param {ObjectOrJsonString} params 请求参数，对象或json字符串
 * @param {string} safeTag 安全标签, 安全等级;时间戳;防重放攻击的nonce, 例如：2;1551687809382;a411a616d438a...
 * @param {string} ticket 会话票据
 * @return {Promise} Promise
 */
const postSafeOfSecurityLevel2 = (apiUri, params, safeTag, ticket) => {
	if (!params) {
		params = {}
	}
	let _param = typeof(params) === 'string' ? params : param2String(params)
	// param:Base64编码后的参数, 前置6位混淆随机字符
	let param = randomString(6) + base64.en(_param)
	// strForSign:待签名字符串
	let strForSign = apiUri + param + ticket + safeTag
	// sign:签名
	let sign = sha256(strForSign)
	let requestBody = {
		param,
		sign
	}
	let isStr = typeof(params) === 'string'
	var safeConfig = createSafeConfig(safeTag, isStr ? 2 : 1)
	var config = {}
	config.method = 'POST'
	config.url = apiServerUrl + apiUri
	config.data = param2String(requestBody)
	config.withCredentials = true
	config.timeout = 60 * 1000
	config.header = safeConfig.headers
	return new Promise((resolve, reject) => {
		uni.request(config).then(response => {
			resolve(response[1])
		}).catch(error => {
			resolve(error[1])
		})
	})
}

/**
 * 3:防重放+AES/RSA加密+签名
 * @param {string} apiUri api uri
 * @param {ObjectOrJsonString} params 请求参数，对象或json字符串
 * @param {string} safeTag 安全标签, 安全等级;时间戳;防重放攻击的nonce, 例如：3;1551687809382;a411a616d438a...
 * @param {string} ticket 会话票据
 * @return {Promise} Promise
 */
const postSafeOfSecurityLevel3 = (apiUri, params, safeTag, ticket) => {
	if (!params) {
		params = {}
	}
	let isStr = typeof(params) === 'string'
	let _param = isStr ? params : JSON.stringify(params)
	// aesKey:新建AES密钥
	let aesKey = newAESKey()
	// param:AES加密后的参数
	let param = encryptByAES(_param, aesKey)
	// key:RSA公钥加密后的AES密钥
	let key = encryptByRSA(aesKey)
	// strForSign:待签名字符串
	let strForSign = apiUri + param + key + ticket + safeTag
	// sign:签名
	let sign = sha256(strForSign)
	let requestBody = {
		param,
		sign,
		key
	}
	var safeConfig = createSafeConfig(safeTag, 3)
	var config = {}
	config.method = 'POST'
	config.url = apiServerUrl + apiUri
	config.data = param2String(requestBody)
	config.withCredentials = true
	config.timeout = 60 * 1000
	config.header = safeConfig.headers
	return new Promise((resolve, reject) => {
		uni.request(config).then(response => {
			resolve(response[1])
		}).catch(error => {
			resolve(error[1])
		})
	})
}

/**
 * 创建安全标签, 安全等级;时间戳;防重放攻击的nonce, 例如：1;1551687809382;a411a616d438a...
 * @param {int} securityLevel 安全等级[0, 1, 2, 3], 如下：
 *    0:无, 1:防重放, 2:防重放+Base64编码+签名, 3:防重放+AES/RSA加密+签名
 * @return {string} 安全标签
 */
const createSafeTag = securityLevel => {
	if (!securityLevel) {
		return null
	}
	let mix = (Math.random() + '').substr(-5)
	let timestamp = new Date().getTime() + '-' + mix
	let nonce = md5(mix + ':' + securityLevel + ':' + timestamp)
	return securityLevel + ';' + timestamp + ';' + nonce
}

/**
 * 创建安全axios配置对象
 * @param {string} safeTag 安全标签, 安全等级;时间戳;防重放攻击的nonce, 例如：1;1551687809382;a411a616d438a...
 * @param {int} paramType 参数类型, [1:对象, 2:json字符串, 3:text字符串] 默认为1
 * @return {object} axios.config对象
 */
const createSafeConfig = (safeTag, paramType = 1) => {
	let headers = {}
	headers['Accept-Language'] = uni.getStorage('lang') || 'zh'
	if (safeTag) {
		headers['SAFE-TAG'] = safeTag
	}
	console.log(paramType)
	if (paramType === 2) {
		headers['Content-Type'] = 'application/json;charset=UTF-8'
	} else if (paramType === 3) {
		headers['Content-Type'] = 'application/x-www-form-urlencoded'
	}
	let _config = { headers }
	return _config
}

/**
 * 下载文件
 * 业务代码中的使用示例：
 * this.$http.download('/common/versionUpdate/download', 'filename.pdf', { aa: 'aaasdf', bb: 1 })
 * @param {String} uri 下载地址
 * @param {String} filename 文件名称
 * @param {Object} params 请求参数
 */
const download = (uri, filename, params) => {
	let userAgent = navigator.userAgent.toLowerCase()
	if (userAgent.indexOf('edge') > -1) {
		// Edge
		downloadGet(uri, params)
		return
	}
	if (userAgent.indexOf('firefox') > -1) {
		// firefox,opera
		downloadSafe(uri, filename, params)
	} else {
		// chrome,IE11,...
		downloadGet(uri, params)
	}
}

/**
 * doc文件下载方法
 * @param {String} uri 下载地址
 * @param {String} filename 文件名
 * @param {Object} params 请求参数
 */
const download2 = (uri, filename, params) => {
	let userAgent = navigator.userAgent.toLowerCase()
	if (userAgent.indexOf('edge') > -1) {
		// Edge
		downloadGet2(uri, params)
		return
	}
	if (userAgent.indexOf('firefox') > -1) {
		// firefox,opera
		downloadSafe(uri, filename, params)
	} else {
		// chrome,IE11,...
		downloadGet2(uri, params)
	}
}

/**
 * 下载文件，get方式，无安全标签，以请求参数方式发送token, 文件名称由服务端响应头控制
 * chrome, 360极速, IE11: down OK
 * firefox down failure
 * @param {String} uri 下载地址
 * @param {Object} params 请求参数
 */
const downloadGet = (uri, params) => {
	return new Promise((resolve, reject) => {
		try {
			let iframe = document.createElement('iframe')
			iframe.style.display = 'none'
			if (!params) {
				params = {}
			}
			// get方式下载文件是链接方式，不是ajax方式，加不了请求头，只能通过请求参数添加会话票据
			params.sumTicket = uni.getStorageSync(TOKEN_NAME)
			let ps = param2String(params)
			if (uri.indexOf('?') !== -1) {
				uri += '&' + ps
			} else {
				if (ps.indexOf('=') !== -1) {
					uri += '?' + ps
				}
			}
			iframe.onload = function() {
				document.body.removeChild(iframe)
			}
			if (uri.indexOf('http') !== 0) {
				iframe.src = apiServerUrl + uri
			} else {
				iframe.src = uri
			}
			setTimeout(() => {
				resolve('ok')
			}, 1000)
			window.addEventListener('message', function(error) {
				let res = JSON.parse(error.data)
				if (
					res &&
					typeof res === 'object' &&
					'code' in res &&
					res.code !== 20000
				) {
					reject(res)
				}
			})
			document.body.appendChild(iframe)
		} catch (e) {
			e
		}
	})
}

const downloadGet2 = (uri, params) => {
	return new Promise((resolve, reject) => {
		try {
			if (!params) {
				params = {}
			}
			// get方式下载文件是链接方式，不是ajax方式，加不了请求头，只能通过请求参数添加会话票据
			params.sumTicket = uni.getStorageSync(TOKEN_NAME)
			let ps = param2String(params)
			if (uri.indexOf('?') !== -1) {
				uri += '&' + ps
			} else {
				if (ps.indexOf('=') !== -1) {
					uri += '?' + ps
				}
			}
			if (uri.indexOf('http') !== 0) {
				uri = apiServerUrl + uri
			}
			let link = document.createElement('a')
			link.href = uri + '&' + new Date().getTime()
			link.target = '_blank'
			document.body.appendChild(link)
			link.click()
			// 释放URL对象
			window.URL.revokeObjectURL(link.href)
			// 删除link对象
			document.body.removeChild(link)
		} catch (e) {
			e
		}
	})
}

/**
 * 将get请求参数转为字符串
 * 示例：{a: 'aa', b: 123} => a=aa&b=123
 * @param {Object/String} params get请求参数
 * @returns {String} 参数字符串
 */
const param2String = params => {
	if (typeof params === 'string') {
		return params
	}
	let ret = ''
	for (let it in params) {
		let val = params[it]
		if (
			typeof val === 'object' && //
			(!(val instanceof Array) ||
				(val.length > 0 && typeof val[0] === 'object'))
		) {
			val = JSON.stringify(val)
		}
		ret += it + '=' + encodeURIComponent(val) + '&'
	}
	if (ret.length > 0) {
		ret = ret.substring(0, ret.length - 1)
	}
	return ret
}

/**
 * 安全下载，post with token
 * chrome,firefox,opera down OK need filename
 * 360极速(blink内核) down OK need filename
 * IE11 down failure
 * @param {String} uri 下载地址
 * @param {String} filename 文件名称
 * @param {Object} params 请求参数
 */
const downloadSafe = (uri, filename, params) => {
	uni.request({
			method: 'POST',
			url: uri,
			data: param2String(params),
			headers: { 'SAFE-TAG': createSafeTag(1) },
			// 指定返回服务器返回的数据类型
			responseType: 'blob'
		})
		.then(response => {
			// 处理返回的文件流
			let blob = response
			if (response.type === 'application/json') {
				uni.showToast({
					message: '文件下载失败',
					duration: 3000,
					type: 'error'
				})
				return
			}
			let reader = new FileReader()
			// 转换为base64，可以直接放入a标签href
			reader.readAsDataURL(blob)
			reader.onload = function(e) {
				let link = document.createElement('a')
				link.download = filename
				link.href = e.target.result
				document.body.appendChild(link)
				link.click()
				// 释放URL对象
				window.URL.revokeObjectURL(link.href)
				// 删除link对象
				document.body.removeChild(link)
			}
		})
		.catch(err => {
			uni.showToast({
				message: '文件下载失败: ' + err,
				duration: 3000,
				type: 'error'
			})
		})
}

// 统一方法输出口
export { post, postSafe, postJson, get, getSafe, download, download2 }
